namespace Hellophp;

class Db {

    const INSTANCE_MAX_LIFETIME = 60;
    private static _instances = [];
    private static _instancesTimeout = [];

    private _dbh = null;
    private _instanceName = null;

	public function __construct(instanceName = "default") {

        let this->_instanceName = instanceName;
        let this->_dbh = self::_getDbh(instanceName);
	}

    private static function _getDbh(instanceName) {

        if (array_key_exists(instanceName, self::_instances)) {
            if (self::_instancesTimeout[instanceName] > time()) {
                return self::_instances[instanceName];
            }
            unset(self::_instances[instanceName]);
        }

        var option, attr;
        let option = Config::get("db.".instanceName);
		let attr = [
			\PDO::ATTR_ERRMODE : \PDO::ERRMODE_EXCEPTION,
			\PDO::ATTR_STRINGIFY_FETCHES : false,
			\PDO::ATTR_EMULATE_PREPARES : false
		];
        var e;
        var dbh;
		try {
			let dbh = new \PDO(option["dsn"], option["user"], option["password"], attr);
			if (!empty(option["charset"])) {
				dbh->exec("SET NAMES ".option["charset"]);
			}
			Logger::log(sprintf("connect to %s succeed.", option["dsn"]));
		} catch \PDOException, e {
			Logger::log(sprintf("connect to %s failed.", option["dsn"]));
			throw new Db\Exception("Connect to db(".option["dsn"].") failed: " . e->getMessage(), Db\Exception::DB_CONNECT_FAILED);
		}

        let self::_instances[instanceName] = dbh;
        let self::_instancesTimeout[instanceName] = time() + self::INSTANCE_MAX_LIFETIME;
        return self::_instances[instanceName];
    }

	public function query(query, params = []) {

        var e;
		try {
            var stmt;
			let stmt = this->_dbh->prepare(query);
			stmt->execute(params);
            var rows;
			let rows = stmt->fetchAll(\PDO::FETCH_ASSOC);
			Logger::log(sprintf("db query: %s (%s) => %s", query, json_encode(params), json_encode(rows)));
			return rows;
		} catch \PDOException, e {
			Logger::log(sprintf("db query failed. query=%s, params=%s", query, json_encode(params)));
			throw new Db\Exception("db query failed: " . e->getMessage(), Db\Exception::DB_QUERY_FAILED);
		}
	}
}
